import { LifeCycles, isInterval, isMyRoom } from '@/ayaka'
import { ProgressCache, SiteCache, SiteInfo, SiteTuple } from './types'
import { BuildableStructureTypes, LowBuildableStructureTypes } from '@/utils/constants'
import RampartController from '../structures/rampart'
import { RoomSafeState } from '@/room/state/types'
import { RoomDangerAreaType } from '../structures/rampart/types'

// 小建筑工地
const smallSiteType = [STRUCTURE_ROAD, STRUCTURE_RAMPART, STRUCTURE_WALL] as BuildableStructureConstant[]

/**
 * 建筑工地管理
 */
export default class StructureSite {
    /**
     * 建筑工地缓存
     */
    protected static siteCache: SiteCache = {}

    /**
     * 房间建筑工地所需进度缓存
     */
    protected static progressCache: ProgressCache = {}

    /**
     * 阻止实例化
     */
    protected constructor() {
        throw new Error('SiteCache 不可被实例化！')
    }

    /**
     * 刷新建筑工地缓存
     */
    public static update(interval = 100, force = false) {
        if (!isInterval(interval) && !force) return

        if (Game._isSiteUpdate) return

        const siteCache = (StructureSite.siteCache = {}) as SiteCache
        const progressCache = (StructureSite.progressCache = {}) as ProgressCache

        Object.values(Game.constructionSites).forEach((site) => {
            const room = site.room
            if (!room) return

            if (!siteCache[room.name]) siteCache[room.name] = {}

            const data = { id: site.id, x: site.pos.x, y: site.pos.y }
            if (!siteCache[room.name]![site.structureType]) {
                siteCache[room.name]![site.structureType] = [data]
            } else {
                siteCache[room.name]![site.structureType]!.push(data)
            }

            if (!progressCache[room.name]) progressCache[room.name] = 0
            progressCache[room.name] += site.progressTotal - site.progress
        })

        Game._isSiteUpdate = true
    }

    /**
     * 获取指定房间的建筑工地缓存
     */
    public static get(roomName: string) {
        return StructureSite.siteCache[roomName]
    }

    /**
     * 选取房间中优先级最高的建筑工地
     */
    public static getFirst(roomName: string, creep: Creep): SiteTuple | null {
        if (!Game._firstSiteInfo) Game._firstSiteInfo = {}
        if (Game._firstSiteInfo[roomName]) return Game._firstSiteInfo[roomName]

        const sites = StructureSite.get(roomName)
        if (!sites) return null

        const room = Game.rooms[roomName]
        const dangerArea =
            isMyRoom(room) && room.memory.state.safe?.state !== RoomSafeState.normal
                ? RampartController.getDangerArea(room)
                : null

        // 低等级优先修路，保障物流速度
        const BuildableTypes =
            room.controller && room.controller.level <= 2 && Object.keys(room.memory.structures.road || {}).length < 40
                ? LowBuildableStructureTypes
                : BuildableStructureTypes

        for (const type of BuildableTypes) {
            const sitesInfo = sites[type]?.filter((site) => {
                // 如果房间处于危险状态，则只建造安全区域内的建筑
                if (dangerArea) {
                    return dangerArea.get(site.x, site.y) === RoomDangerAreaType.safe && !site.done
                }
                return !site.done
            })
            if (!sitesInfo || !sitesInfo.length) continue

            if (!smallSiteType.includes(type)) {
                Game._firstSiteInfo[roomName] = [type, sitesInfo[0]]
                return Game._firstSiteInfo[roomName]
            }

            // 找到对于爬来说最近的工地
            const minSite = StructureSite.getClosestSite(creep, sitesInfo)
            return [type, minSite!]
        }

        return null
    }

    /**
     * 找到对于爬来说最近的工地
     */
    public static getClosestSite(creep: Creep, sitesInfo: SiteInfo[]): SiteInfo | null {
        let minDistance = Infinity
        let minSite: SiteInfo | null = null
        for (const site of sitesInfo) {
            const distance = creep.pos.getRangeTo(site.x, site.y)
            if (distance < minDistance) {
                minDistance = distance
                minSite = site
            }
        }
        return minSite
    }

    /**
     * 获取指定房间的建筑工地所需进度缓存
     */
    public static getProgress(roomName: string) {
        return StructureSite.progressCache[roomName] || 0
    }

    /**
     * 设置某个建筑工地已经完成
     */
    public static setDone(roomName: string, type: BuildableStructureConstant, id: string) {
        const sitesInfo = StructureSite.get(roomName)?.[type]
        if (!sitesInfo) return
        const index = sitesInfo.findIndex((site) => site.id === id)
        if (index === -1) return
        sitesInfo[index].done = true
    }
}

export const updateStructureSite: LifeCycles = {
    tickStart() {
        StructureSite.update()
    },
}

declare global {
    interface Game {
        /**
         * 本 tick 是否已经更新过建筑工地缓存，防止重复更新
         */
        _isSiteUpdate?: boolean
        /**
         * 本 tick 返回的优先级最高的建筑工地 id
         */
        _firstSiteInfo?: {
            [roomName: string]: SiteTuple | null
        }
    }
}
